package org.bjf.modules.wechatpay.util;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

/**
 * @author bjf
 */
@Slf4j
public class PayUtil {

  private static final String SIGN_KEY = "goodboy";
  // = Application.sContext.getBean(Environment.class).getProperty("secretKey");

  public static String sign(Map<String, String> param) {
    return sign(param, SIGN_KEY);
  }

  public static String sign(Object obj, String signKey) {
    Map<String, String> fieldMap = getFieldMap(obj, Boolean.TRUE);
    return sign(fieldMap, signKey);
  }

  public static String sign(Map<String, String> param, String signKey) {

    log.info("签名前数据:{}", JSONObject.toJSONString(param));

    //===1.按key字典排序及移除不参与签名的字段
    TreeMap<String, String> sortedMap = new TreeMap<>(param);
    sortedMap.remove("sign");

    //===2.组装签名字符串
    StringBuilder sb = new StringBuilder();
    for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
      String value = entry.getValue();
      if (StringUtils.isNotBlank(value)) {
        //空串不参与签名
        sb.append(entry.getKey()).append("=").append(value).append("&");
      }
    }
    sb.append("key=").append(signKey);

    //===3.MD5签名
    log.info("签名字符串:{}", sb.toString());
    String sign = DigestUtils.md5Hex(sb.toString()).toUpperCase();
    log.info("MD5签名:{}", sign);
    return sign;
  }


  public static String objectToXml(Object obj) {
    //===1、生成document根节点
    Document doc = DocumentHelper.createDocument();
    Element root = doc.addElement("xml");

    //===2、取对象key-value
    Map<String, String> fieldMap = getFieldMap(obj, Boolean.FALSE);
    for (Entry<String, String> entry : fieldMap.entrySet()) {
      // 生成document文档
      root.addElement(entry.getKey()).addText(String.valueOf(entry.getValue()));
    }

    return doc.asXML();
  }

  public static <T> T xmlToObject(String xmlStr, Class<T> clz) {

    try {
      Document doc = DocumentHelper.parseText(xmlStr);
      Element xml = doc.getRootElement();
      T obj = clz.newInstance();
      Iterator<Element> ite = xml.elementIterator();
      while (ite.hasNext()) {
        Element ele = ite.next();
        String name = ele.getName();
        String value = ele.getText();
        BeanUtils.setProperty(obj, name, value);
      }
      return obj;
    } catch (Exception e) {
      log.error("xml to object 转换出错：" + e.getMessage(), e);
    }
    return null;
  }

  /**
   * 根据对象取Map,空值跳过
   */
  private static Map<String, String> getFieldMap(Object obj, boolean ignoreNull) {
    Class clz = obj.getClass();
    //====1.取当前类字段
    Map<String, String> fieldMap = getFieldMap(obj, clz.getDeclaredFields(), ignoreNull);

    //====2.取父类字段
    Map<String, String> supperFieldMap = getFieldMap(obj, clz.getSuperclass().getDeclaredFields(),
        ignoreNull);

    fieldMap.putAll(supperFieldMap);

    return fieldMap;
  }

  private static Map<String, String> getFieldMap(Object obj, Field[] fields, boolean ignoreNull) {
    Map<String, String> params = new HashMap<>();
    //====1.取当前类字段
    for (Field field : fields) {
      try {
        field.setAccessible(Boolean.TRUE);
        Object value = field.get(obj);
        // 空值跳过
        if (value == null || field.isAnnotationPresent(JsonIgnore.class)) {
          if (ignoreNull) {
            continue;
          } else {
            value = "";
          }
        }
        // 取字段别名
        String name = field.getName();
        if (field.isAnnotationPresent(JSONField.class)) {
          JSONField jsonField = field.getAnnotation(JSONField.class);
          String aliasName = jsonField.name();
          if (StringUtils.isNotBlank(aliasName)) {
            name = aliasName;
          }
        }
        params.put(name, String.valueOf(value));
      } catch (IllegalAccessException e) {
        log.error(e.getMessage(), e);
      }
    }
    return params;
  }
}
